import * as fs from 'fs';
import * as Path from 'path';

function createFolderRecursive(folderPath) {
  if (!fs.existsSync(folderPath)) {
    // 递归创建父级文件夹
    createFolderRecursive(Path.dirname(folderPath));
    fs.mkdirSync(folderPath);
  }
}


class Table<Record> {
  private maxLength = 100;
  private fileRoot: string;
  private tempPath: string;

  constructor(name: string, root: string) {
    const fileRoot = Path.join(root, name)
    const tempPath = Path.join(fileRoot, 'temp.json')
    // 创建表储存根目录
    if (!fs.existsSync(fileRoot)) {
      createFolderRecursive(fileRoot);
    }
    // 创建临时数据文件
    if (!fs.existsSync(tempPath)) {
      fs.writeFileSync(tempPath, '[]');
    }
    this.fileRoot = fileRoot
    this.tempPath = tempPath

  }
  private async getAllFilesInFolder(folderPath: string) {
    const files = fs.readdirSync(folderPath);
    return files;
  }
  private async readFile(path: string): Promise<Array<Record & { id: number }>> {
    const d = fs.readFileSync(path, 'utf8')
    const list: Array<Record & { id: number }> = JSON.parse(d)
    return list;
  }
  // 获取id所在行，所在文件，所在文件数据
  private async getRow(id: number): Promise<{ path: string, list: Array<Record & { id: number }>, index: number } | void> {
    let path = Path.join(this.fileRoot, `${Math.floor(id / this.maxLength)}.json`)
    if (!fs.existsSync(path)) {
      path = this.tempPath;
    }
    const list = await this.readFile(path);
    const index = list.findIndex(e => e.id == id)
    if (index > -1) return { path, list, index }
  }
  public async add(data: Record): Promise<number> {
    const newData = {
      ...data,
      id: -1,
    }
    let count = 0
    const files = await this.getAllFilesInFolder(this.fileRoot);
    files.forEach(e => e == "temp.json" ? '' : count++)
    const temp = await this.readFile(this.tempPath)
    // 数据量超限
    if (temp.length >= this.maxLength) {
      count++
      fs.writeFileSync(Path.join(this.fileRoot, `${count}.json`), JSON.stringify(temp));
      temp.length = 0;
    }
    newData.id = this.maxLength * count + temp.length + 1;
    temp.push(newData)
    fs.writeFileSync(this.tempPath, JSON.stringify(temp));
    return newData.id;
  }
  public async del(id: number) {
    if (!id) throw new Error('id is required');
    const data = await this.getRow(id)
    if (!data) throw new Error('record not existent');
    const list = data.list;
    list.splice(data.index, 1)
    fs.writeFileSync(data.path, JSON.stringify(list));
  }
  public async update(data: Partial<Record & { id?: number }>, id?: number) {
    if (!id) id = data.id
    if (!id) throw new Error('id is required');
    const res = await this.getRow(id)
    if (!res) throw new Error('record not existent');
    // TODO:考虑要不要加上如果不存在id,则自动添加
    const list = res.list
    list[res.index] = {
      ...list[res.index],
      ...data,
      id: list[res.index].id
    }
    fs.writeFileSync(res.path, JSON.stringify(list));
  }
  public async get(id: number | string,field?:string): Promise<Partial<Record & { id: number }> | null> {
    if (!id) throw new Error('id is required');
    const res = await this.getRow(Number(id))
    if (!res) return null;
    if(field){
      const arr=field.split(',')
      const data=res.list[res.index]
      const newData={}
      arr.forEach((k)=>newData[k]=data[k])
      return newData;
    }
    return res.list[res.index]
  }
  public async where(query: Partial<Record>): Promise<Array<Record & { id: number }>> {
    const files = await this.getAllFilesInFolder(this.fileRoot);
    const res: Array<Record & { id: number }> = []
    for (const file of files) {
      const path = Path.join(this.fileRoot, file)
      const list = await this.readFile(path)
      list.forEach(e => {
        for (const key in query) {
          if (query[key] != e[key]) return;
        }
        res.push(e)
      })
    }
    return res;
  }
  public async find(query: Partial<Record>): Promise<Record & { id: number } | undefined> {
    const files = await this.getAllFilesInFolder(this.fileRoot);
    for (const file of files) {
      const path = Path.join(this.fileRoot, file)
      const list = await this.readFile(path)
      for (const item of list) {
        let b
        for (const key in query) {
          if (query[key] != item[key]) {
            b = true
            continue
          };
        }
        if (b) continue
        return item;
      }
    }
  }
  public async custom(call: (list: Array<Record & { id: number }>, path: string) => Promise<any> | any) {
    const files = await this.getAllFilesInFolder(this.fileRoot);
    for (const file of files) {
      const path = Path.join(this.fileRoot, file)
      const list = await this.readFile(path)
      await call(list, path)
    }
  }
}


export default class FsDataBase {
  private fileRoot: string;
  constructor(fileRoot: string) {
    this.fileRoot = fileRoot;
    if (!fs.existsSync(fileRoot)) {
      createFolderRecursive(fileRoot);
    }
  }
  public getTable<T>(name: string): Table<T> {
    return new Table<T>(name, this.fileRoot);
  }
}

